import {TableConfig} from "../common";

export class DbClientNotFoundError extends Error {
    constructor(message = 'No result!') {
        super(message);
        this.name = 'DbClientNotFoundError';
    }
}

export abstract class Transaction {
    abstract oneOrNone(sql: string): Promise<any>;
    abstract one(sql: string): Promise<any>;
    abstract query(sql: string): Promise<any>;
    abstract none(sql: string): Promise<void>;
}

export type DbConfig = {
    host: string;
    port: number;
    database: string;
    user: string;
    password: string;
    connectionLimit?: number;
};

export type TxFun = (transaction: Transaction) => Promise<any>;

export abstract class DbClient {
    protected host: string;
    protected port: number;
    protected database: string;
    protected user: string;
    protected password: string;

    protected constructor({
                              host,
                              port,
                              database,
                              user,
                              password
                          }: DbConfig) {
        this.host = host;
        this.port = port;
        this.database = database;
        this.user = user;
        this.password = password;
    }

    abstract oneOrNone(sql: string, transaction?: Transaction): Promise<any>;
    abstract one(sql: string, transaction?: Transaction): Promise<any>;
    abstract query(sql: string, transaction?: Transaction): Promise<any>;
    abstract none(sql: string, transaction?: Transaction): Promise<void>;
    abstract tx(func: TxFun): Promise<any>;
    abstract connect(): Promise<void>;
    abstract insertSql(data: object, cs: any, tableConfig: TableConfig): string;
    abstract updateSql(data: object, cs: any, tableConfig: TableConfig): string;
    abstract setConnection(connection: any): void;

    columnProcess(s: string): string {
        return s;
    }

    returnJsonProcess(result: any, tableConfig: TableConfig): void {
        if (tableConfig.jsonColumn) {
            tableConfig.jsonColumn.forEach(it => {
                if (result[it]) {
                    try {
                        result[it] = JSON.parse(result[it]);
                    } catch (e) {
                        console.error(`Failed to parse JSON for column ${it}:`, e);
                    }
                }
            });
        }
    }

    protected escapeValue(val: any): string {
        if (val === null || val === undefined) {
            return 'NULL';
        }
        if (typeof val === "string") {
            return `'${val.replace(/'/g, "''")}'`;
        }
        if (typeof val === "boolean") {
            return val ? 'TRUE' : 'FALSE';
        }
        if (typeof val === "number") {
            return val.toString();
        }
        if (val instanceof Date) {
            return `'${val.toISOString()}'`;
        }
        return `'${JSON.stringify(val).replace(/'/g, "''")}'`;
    }
}